{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "36972001-cbed-46f6-8bed-e57feec3bbd4",
   "metadata": {},
   "source": [
    "# 作业 \n",
    "## 使用领域（私有）数据微调 ChatGLM3\n",
    "\n",
    "生成带有 epoch 和 timestamp 的模型文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "f2386615-d1d6-40c9-a014-b2bce85838ab",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "PyTorch built with:\n",
      "  - GCC 9.3\n",
      "  - C++ Version: 201703\n",
      "  - Intel(R) oneAPI Math Kernel Library Version 2022.2-Product Build 20220804 for Intel(R) 64 architecture applications\n",
      "  - Intel(R) MKL-DNN v3.3.2 (Git Hash 2dc95a2ad0841e29db8b22fbccaf3e5da7992b01)\n",
      "  - OpenMP 201511 (a.k.a. OpenMP 4.5)\n",
      "  - LAPACK is enabled (usually provided by MKL)\n",
      "  - NNPACK is enabled\n",
      "  - CPU capability usage: AVX2\n",
      "  - CUDA Runtime 12.1\n",
      "  - NVCC architecture flags: -gencode;arch=compute_50,code=sm_50;-gencode;arch=compute_60,code=sm_60;-gencode;arch=compute_70,code=sm_70;-gencode;arch=compute_75,code=sm_75;-gencode;arch=compute_80,code=sm_80;-gencode;arch=compute_86,code=sm_86;-gencode;arch=compute_90,code=sm_90\n",
      "  - CuDNN 8.9.2\n",
      "  - Magma 2.6.1\n",
      "  - Build settings: BLAS_INFO=mkl, BUILD_TYPE=Release, CUDA_VERSION=12.1, CUDNN_VERSION=8.9.2, CXX_COMPILER=/opt/rh/devtoolset-9/root/usr/bin/c++, CXX_FLAGS= -D_GLIBCXX_USE_CXX11_ABI=0 -fabi-version=11 -fvisibility-inlines-hidden -DUSE_PTHREADPOOL -DNDEBUG -DUSE_KINETO -DLIBKINETO_NOROCTRACER -DUSE_FBGEMM -DUSE_QNNPACK -DUSE_PYTORCH_QNNPACK -DUSE_XNNPACK -DSYMBOLICATE_MOBILE_DEBUG_HANDLE -O2 -fPIC -Wall -Wextra -Werror=return-type -Werror=non-virtual-dtor -Werror=bool-operation -Wnarrowing -Wno-missing-field-initializers -Wno-type-limits -Wno-array-bounds -Wno-unknown-pragmas -Wno-unused-parameter -Wno-unused-function -Wno-unused-result -Wno-strict-overflow -Wno-strict-aliasing -Wno-stringop-overflow -Wsuggest-override -Wno-psabi -Wno-error=pedantic -Wno-error=old-style-cast -Wno-missing-braces -fdiagnostics-color=always -faligned-new -Wno-unused-but-set-variable -Wno-maybe-uninitialized -fno-math-errno -fno-trapping-math -Werror=format -Wno-stringop-overflow, LAPACK_INFO=mkl, PERF_WITH_AVX=1, PERF_WITH_AVX2=1, PERF_WITH_AVX512=1, TORCH_VERSION=2.2.2, USE_CUDA=ON, USE_CUDNN=ON, USE_EXCEPTION_PTR=1, USE_GFLAGS=OFF, USE_GLOG=OFF, USE_MKL=ON, USE_MKLDNN=ON, USE_MPI=OFF, USE_NCCL=1, USE_NNPACK=ON, USE_OPENMP=ON, USE_ROCM=OFF, USE_ROCM_KERNEL_ASSERT=OFF, \n",
      " _CudaDeviceProperties(name='NVIDIA GeForce RTX 4090', major=8, minor=9, total_memory=24209MB, multi_processor_count=128)\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "\n",
    "print(torch.__config__.show(), torch.cuda.get_device_properties(0))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "280d5f7b-dada-49e1-81ee-d28a32900423",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义全局变量和参数\n",
    "model_name_or_path = 'THUDM/chatglm3-6b'  # 模型ID或本地路径\n",
    "# train_data_path = 'data/zhouyi_dataset_handmade.csv'    # 训练数据路径\n",
    "train_data_path = 'data/zhouyi_dataset_20240118_163659.csv'    # 训练数据路径(批量生成数据集）\n",
    "eval_data_path = None                     # 验证数据路径，如果没有则设置为None\n",
    "seed = 8                                 # 随机种子\n",
    "max_input_length = 512                    # 输入的最大长度\n",
    "max_output_length = 1536                  # 输出的最大长度\n",
    "lora_rank = 16                             # LoRA秩\n",
    "lora_alpha = 32                           # LoRA alpha值\n",
    "lora_dropout = 0.05                       # LoRA Dropout率\n",
    "prompt_text = ''                          # 所有数据前的指令文本"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "461e2caf-4dd4-4d4d-a8f6-7bfd674d5754",
   "metadata": {},
   "source": [
    "## 数据处理"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "2c4d167c-0f39-4b11-8fad-ec608c2b3c3e",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/root/miniconda3/envs/peft/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n",
      "Generating train split: 160 examples [00:00, 38361.07 examples/s]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "DatasetDict({\n",
      "    train: Dataset({\n",
      "        features: ['content', 'summary'],\n",
      "        num_rows: 160\n",
      "    })\n",
      "})\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "from datasets import load_dataset\n",
    "\n",
    "dataset = load_dataset(\"csv\", data_files=train_data_path)\n",
    "print(dataset)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "4d02d0c6-fc78-429c-9193-1453a91aed81",
   "metadata": {},
   "outputs": [],
   "source": [
    "from datasets import ClassLabel, Sequence\n",
    "import random\n",
    "import pandas as pd\n",
    "from IPython.display import display, HTML\n",
    "\n",
    "def show_random_elements(dataset, num_examples=10):\n",
    "    assert num_examples <= len(dataset), \"Can't pick more elements than there are in the dataset.\"\n",
    "    picks = []\n",
    "    for _ in range(num_examples):\n",
    "        pick = random.randint(0, len(dataset)-1)\n",
    "        while pick in picks:\n",
    "            pick = random.randint(0, len(dataset)-1)\n",
    "        picks.append(pick)\n",
    "    \n",
    "    df = pd.DataFrame(dataset[picks])\n",
    "    for column, typ in dataset.features.items():\n",
    "        if isinstance(typ, ClassLabel):\n",
    "            df[column] = df[column].transform(lambda i: typ.names[i])\n",
    "        elif isinstance(typ, Sequence) and isinstance(typ.feature, ClassLabel):\n",
    "            df[column] = df[column].transform(lambda x: [typ.feature.names[i] for i in x])\n",
    "    display(HTML(df.to_html()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "4787d6f7-27ab-4822-b64e-2794bf52ec91",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>content</th>\n",
       "      <th>summary</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>请描述师卦的含义。</td>\n",
       "      <td>在周易中，师卦是一个由坎卦（水）和坤卦（地）相叠而成的异卦。这一卦象代表着军队的力量和军情的总指挥，预示着吉祥无灾。象辞中描述了地中有水的情景，寓意着君子应当像大地一样容纳和畜养大众。师卦的解释强调选择德高望重的长者来统率军队，才能获得吉祥无咎。另外，师卦也象征着困难重重，需要包容别人、艰苦努力，及时行事，严于律已。在事业、经商、求名、婚恋等方面的决策中，都需要警惕潜在敌人，小心谨慎，合作与决断兼顾，方能成功。</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>需卦在周易中代表了哪些方面？</td>\n",
       "      <td>需卦是一个占卜卦象，在周易卦象中，它由下卦乾和上卦坎组成。坎象征着云，乾象征着天，云聚于天，形成了等待时机的卦象。这是一个大吉大利的卜问，特别适合涉水渡河。根据《象辞》，君子观此卦象，可以宴饮安乐，待时而动。\\n\\n需卦的核心哲学是：稳扎稳打，不可冒失行动，观时待变，耐心等待。在事业上，需要审时度势，守中正，不可急进，自信充满，可化险为夷。在经商中，必须充满耐心，创造条件和机会，行事光明磊落，等到时机成熟后必然一帆风顺。在感情方面，亦需慎重，培养感情，以诚实、热情相待，时机成熟后可以有良好的结果。\\n\\n需卦紧随坤宫游魂卦，预示着踌躇和期待。得此卦者，需要时机尚未成熟，需要耐心等待，急进反会见凶险。</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>蒙卦是怎样的一个卦象？</td>\n",
       "      <td>蒙卦是由艮卦（山）下，坎卦（水）上组成的异卦相叠。它代表着通泰，启蒙的意义。在这里，卜者并非是在向幼稚愚昧的人取求，而是幼稚愚昧的人在向卜者求教。第一次卜筮就得到了神灵的指示。然而，如果轻慢不敬地再三卜筮的话，神灵便不会再示警。总的来说，这是一个吉利的卜问。\\n\\n蒙卦的核心在于山下有泉的形象，寓意着启蒙。君子观此卦象，应当以果敢坚毅的行动来培养自身的品德，像山泉一样果断行动。然而，此卦乃是离宫四世卦，它代表着回还往复、疑惑不前、多忧愁过失，因而属于凶卦。\\n\\n蒙卦在个人发展、事业经商、求名婚恋等方面的解释不一。在事业方面，表示事业初建，具有启蒙和通达之象，需要勇敢坚毅的行动；而在经商方面，需要务必小心谨慎，树立高尚的商业道德，不可急功近利；求名方面，需要接受良好的基础教育，陶冶情操。整体而言，此卦提示须忍耐待机而动，听取别人意见，方能通达运势。</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>需卦代表什么？</td>\n",
       "      <td>需卦是一个占卜卦象，在周易卦象中，它由下卦乾和上卦坎组成。坎象征着云，乾象征着天，云聚于天，形成了等待时机的卦象。这是一个大吉大利的卜问，特别适合涉水渡河。根据《象辞》，君子观此卦象，可以宴饮安乐，待时而动。\\n\\n需卦的核心哲学是：稳扎稳打，不可冒失行动，观时待变，耐心等待。在事业上，需要审时度势，守中正，不可急进，自信充满，可化险为夷。在经商中，必须充满耐心，创造条件和机会，行事光明磊落，等到时机成熟后必然一帆风顺。在感情方面，亦需慎重，培养感情，以诚实、热情相待，时机成熟后可以有良好的结果。\\n\\n需卦紧随坤宫游魂卦，预示着踌躇和期待。得此卦者，需要时机尚未成熟，需要耐心等待，急进反会见凶险。</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>周易的屯卦讲述了什么？</td>\n",
       "      <td>在周易中，屯卦是一个大吉大利的卦象，预示着吉祥和大利。然而，不利于出门，但有利于建国封侯。屯卦由上卦坎（水）下卦震（雷）组成，坎为云，震为雷。预示着云行雷动的卦象。君子观此卦象，取法于云雷，用云的恩泽，雷的威严来治理国事。屯卦象征着开始困难，需要毅力和果敢才能获得吉利。身处困境需要多加辛苦努力，排除困难，方可通达，有初难后解之象。因此，对于事业创业而言，应当小心翼翼，勇往直前，灵活机动，可望获得大的成功。但也需注意到仍有困难存在，务必有他人相助，平时应多施恩惠。对于经商，起初多有挫折，必须坚定信念，积极进取，行动果断，若仍无法摆脱困境，则应退守保全，等待机会，再展宏图。对于婚恋，好事多磨，忠贞纯洁，大胆追求，能够成功。屯卦的核心哲学在于，初难后解，需要毅力和坚忍不拔的毅力和锲而不舍的奋斗精神，但也需得到贤德之人的帮助才能摆脱困境。</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "show_random_elements(dataset[\"train\"], num_examples=5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "b99c93ee-dfaf-4f38-b499-202accf20a1d",
   "metadata": {},
   "outputs": [],
   "source": [
    "from transformers import AutoTokenizer\n",
    "\n",
    "tokenizer = AutoTokenizer.from_pretrained(model_name_or_path,\n",
    "                                          trust_remote_code=True,\n",
    "                                          revision='b098244')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "c680f5af-4c73-4f40-adfa-0e404e10d244",
   "metadata": {},
   "outputs": [],
   "source": [
    "# tokenize_func 函数\n",
    "def tokenize_func(example, tokenizer, ignore_label_id=-100):\n",
    "    \"\"\"\n",
    "    对单个数据样本进行tokenize处理。\n",
    "\n",
    "    参数:\n",
    "    example (dict): 包含'content'和'summary'键的字典，代表训练数据的一个样本。\n",
    "    tokenizer (transformers.PreTrainedTokenizer): 用于tokenize文本的tokenizer。\n",
    "    ignore_label_id (int, optional): 在label中用于填充的忽略ID，默认为-100。\n",
    "\n",
    "    返回:\n",
    "    dict: 包含'tokenized_input_ids'和'labels'的字典，用于模型训练。\n",
    "    \"\"\"\n",
    "\n",
    "    # 构建问题文本\n",
    "    question = prompt_text + example['content']\n",
    "    if example.get('input', None) and example['input'].strip():\n",
    "        question += f'\\n{example[\"input\"]}'\n",
    "\n",
    "    # 构建答案文本\n",
    "    answer = example['summary']\n",
    "\n",
    "    # 对问题和答案文本进行tokenize处理\n",
    "    q_ids = tokenizer.encode(text=question, add_special_tokens=False)\n",
    "    a_ids = tokenizer.encode(text=answer, add_special_tokens=False)\n",
    "\n",
    "    # 如果tokenize后的长度超过最大长度限制，则进行截断\n",
    "    if len(q_ids) > max_input_length - 2:  # 保留空间给gmask和bos标记\n",
    "        q_ids = q_ids[:max_input_length - 2]\n",
    "    if len(a_ids) > max_output_length - 1:  # 保留空间给eos标记\n",
    "        a_ids = a_ids[:max_output_length - 1]\n",
    "\n",
    "    # 构建模型的输入格式\n",
    "    input_ids = tokenizer.build_inputs_with_special_tokens(q_ids, a_ids)\n",
    "    question_length = len(q_ids) + 2  # 加上gmask和bos标记\n",
    "\n",
    "    # 构建标签，对于问题部分的输入使用ignore_label_id进行填充\n",
    "    labels = [ignore_label_id] * question_length + input_ids[question_length:]\n",
    "\n",
    "    return {'input_ids': input_ids, 'labels': labels}\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "b99af28d-86b1-41c1-84da-2d2a4efc200f",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Map: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 160/160 [00:00<00:00, 3298.24 examples/s]\n"
     ]
    }
   ],
   "source": [
    "column_names = dataset['train'].column_names\n",
    "tokenized_dataset = dataset['train'].map(\n",
    "    lambda example: tokenize_func(example, tokenizer),\n",
    "    batched=False, \n",
    "    remove_columns=column_names\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "b749ba67-e890-4a42-a652-1eacf030ad47",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Flattening the indices: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 160/160 [00:00<00:00, 19659.26 examples/s]\n"
     ]
    }
   ],
   "source": [
    "tokenized_dataset = tokenized_dataset.shuffle(seed=seed)\n",
    "tokenized_dataset = tokenized_dataset.flatten_indices()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "5fb9d942-e138-4ff4-a475-65d6e4f7d0c3",
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "from typing import List, Dict, Optional\n",
    "\n",
    "# DataCollatorForChatGLM 类\n",
    "class DataCollatorForChatGLM:\n",
    "    \"\"\"\n",
    "    用于处理批量数据的DataCollator，尤其是在使用 ChatGLM 模型时。\n",
    "\n",
    "    该类负责将多个数据样本（tokenized input）合并为一个批量，并在必要时进行填充(padding)。\n",
    "\n",
    "    属性:\n",
    "    pad_token_id (int): 用于填充(padding)的token ID。\n",
    "    max_length (int): 单个批量数据的最大长度限制。\n",
    "    ignore_label_id (int): 在标签中用于填充的ID。\n",
    "    \"\"\"\n",
    "\n",
    "    def __init__(self, pad_token_id: int, max_length: int = 2048, ignore_label_id: int = -100):\n",
    "        \"\"\"\n",
    "        初始化DataCollator。\n",
    "\n",
    "        参数:\n",
    "        pad_token_id (int): 用于填充(padding)的token ID。\n",
    "        max_length (int): 单个批量数据的最大长度限制。\n",
    "        ignore_label_id (int): 在标签中用于填充的ID，默认为-100。\n",
    "        \"\"\"\n",
    "        self.pad_token_id = pad_token_id\n",
    "        self.ignore_label_id = ignore_label_id\n",
    "        self.max_length = max_length\n",
    "\n",
    "    def __call__(self, batch_data: List[Dict[str, List]]) -> Dict[str, torch.Tensor]:\n",
    "        \"\"\"\n",
    "        处理批量数据。\n",
    "\n",
    "        参数:\n",
    "        batch_data (List[Dict[str, List]]): 包含多个样本的字典列表。\n",
    "\n",
    "        返回:\n",
    "        Dict[str, torch.Tensor]: 包含处理后的批量数据的字典。\n",
    "        \"\"\"\n",
    "        # 计算批量中每个样本的长度\n",
    "        len_list = [len(d['input_ids']) for d in batch_data]\n",
    "        batch_max_len = max(len_list)  # 找到最长的样本长度\n",
    "\n",
    "        input_ids, labels = [], []\n",
    "        for len_of_d, d in sorted(zip(len_list, batch_data), key=lambda x: -x[0]):\n",
    "            pad_len = batch_max_len - len_of_d  # 计算需要填充的长度\n",
    "            # 添加填充，并确保数据长度不超过最大长度限制\n",
    "            ids = d['input_ids'] + [self.pad_token_id] * pad_len\n",
    "            label = d['labels'] + [self.ignore_label_id] * pad_len\n",
    "            if batch_max_len > self.max_length:\n",
    "                ids = ids[:self.max_length]\n",
    "                label = label[:self.max_length]\n",
    "            input_ids.append(torch.LongTensor(ids))\n",
    "            labels.append(torch.LongTensor(label))\n",
    "\n",
    "        # 将处理后的数据堆叠成一个tensor\n",
    "        input_ids = torch.stack(input_ids)\n",
    "        labels = torch.stack(labels)\n",
    "\n",
    "        return {'input_ids': input_ids, 'labels': labels}\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "478eec3d-3b00-4d67-9bdc-88535c10ca27",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 准备数据整理器\n",
    "data_collator = DataCollatorForChatGLM(pad_token_id=tokenizer.pad_token_id)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d0ad8947-cfb8-47b0-90fd-5a8c93eed85a",
   "metadata": {},
   "source": [
    "## 加载模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "102589d4-f07c-4952-abf2-42eed291c01d",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Loading checkpoint shards: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 7/7 [00:01<00:00,  5.14it/s]\n"
     ]
    }
   ],
   "source": [
    "from transformers import AutoModel, BitsAndBytesConfig\n",
    "\n",
    "_compute_dtype_map = {\n",
    "    'fp32': torch.float32,\n",
    "    'fp16': torch.float16,\n",
    "    'bf16': torch.bfloat16\n",
    "}\n",
    "\n",
    "# QLoRA 量化配置\n",
    "q_config = BitsAndBytesConfig(load_in_4bit=True,\n",
    "                              bnb_4bit_quant_type='nf4',\n",
    "                              bnb_4bit_use_double_quant=True,\n",
    "                              bnb_4bit_compute_dtype=_compute_dtype_map['bf16'])\n",
    "# 加载量化后模型\n",
    "model = AutoModel.from_pretrained(model_name_or_path,\n",
    "                                  quantization_config=q_config,\n",
    "                                  device_map='auto',\n",
    "                                  trust_remote_code=True,\n",
    "                                  revision='b098244')\n",
    "\n",
    "model.supports_gradient_checkpointing = True  \n",
    "model.gradient_checkpointing_enable()\n",
    "model.enable_input_require_grads()\n",
    "\n",
    "model.config.use_cache = False  # silence the warnings. Please re-enable for inference!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "28ce4c2d-d2a3-4e0a-a6a8-b66c8901b6a4",
   "metadata": {},
   "outputs": [],
   "source": [
    "from peft import TaskType, LoraConfig, get_peft_model, prepare_model_for_kbit_training\n",
    "from peft.utils import TRANSFORMERS_MODELS_TO_LORA_TARGET_MODULES_MAPPING\n",
    "\n",
    "kbit_model = prepare_model_for_kbit_training(model)\n",
    "target_modules = TRANSFORMERS_MODELS_TO_LORA_TARGET_MODULES_MAPPING['chatglm']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "5f5c327f-c34d-4c22-a72f-7a40757514b6",
   "metadata": {},
   "outputs": [],
   "source": [
    "lora_config = LoraConfig(\n",
    "    target_modules=target_modules,\n",
    "    r=lora_rank,\n",
    "    lora_alpha=lora_alpha,\n",
    "    lora_dropout=lora_dropout,\n",
    "    bias='none',\n",
    "    inference_mode=False,\n",
    "    task_type=TaskType.CAUSAL_LM\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "8c90d7ca-de06-416c-971d-b416e0f52ebd",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "trainable params: 3,899,392 || all params: 6,247,483,392 || trainable%: 0.06241540401681151\n"
     ]
    }
   ],
   "source": [
    "qlora_model = get_peft_model(kbit_model, lora_config)\n",
    "qlora_model.print_trainable_parameters()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "26f3c6c2-28b5-4d2d-b611-a4c3bf90f133",
   "metadata": {},
   "source": [
    "### QLoRA 微调模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "1f4233fa-adee-4fa2-9893-bb149636208b",
   "metadata": {},
   "outputs": [],
   "source": [
    "import datetime\n",
    "\n",
    "timestamp = datetime.datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
    "\n",
    "train_epochs = 3\n",
    "output_dir = f\"models/{model_name_or_path}-epoch{train_epochs}-{timestamp}\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "4f07e78f-8ff8-439a-8728-5b18b6a7626d",
   "metadata": {},
   "outputs": [],
   "source": [
    "from transformers import TrainingArguments, Trainer\n",
    "\n",
    "training_args = TrainingArguments(\n",
    "    output_dir=output_dir,                            # 输出目录\n",
    "    per_device_train_batch_size=8,                     # 每个设备的训练批量大小\n",
    "    gradient_accumulation_steps=1,                     # 梯度累积步数\n",
    "    learning_rate=1e-3,                                # 学习率\n",
    "    num_train_epochs=train_epochs,                     # 训练轮数\n",
    "    lr_scheduler_type=\"linear\",                        # 学习率调度器类型\n",
    "    warmup_ratio=0.1,                                  # 预热比例\n",
    "    logging_steps=1,                                 # 日志记录步数\n",
    "    save_strategy=\"steps\",                             # 模型保存策略\n",
    "    save_steps=10,                                    # 模型保存步数\n",
    "    optim=\"adamw_torch\",                               # 优化器类型\n",
    "    fp16=True,                                        # 是否使用混合精度训练\n",
    ")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "12d86b61-0cf3-4b87-b866-3e81f58ad064",
   "metadata": {},
   "outputs": [],
   "source": [
    "trainer = Trainer(\n",
    "        model=qlora_model,\n",
    "        args=training_args,\n",
    "        train_dataset=tokenized_dataset,\n",
    "        data_collator=data_collator\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "2bc298ef-3c6d-45cb-98d8-ccac43d386d2",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/root/miniconda3/envs/peft/lib/python3.10/site-packages/torch/utils/checkpoint.py:460: UserWarning: torch.utils.checkpoint: please pass in use_reentrant=True or use_reentrant=False explicitly. The default value of use_reentrant will be updated to be False in the future. To maintain current behavior, pass use_reentrant=True. It is recommended that you use use_reentrant=False. Refer to docs for more details on the differences between the two variants.\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "\n",
       "    <div>\n",
       "      \n",
       "      <progress value='60' max='60' style='width:300px; height:20px; vertical-align: middle;'></progress>\n",
       "      [60/60 00:59, Epoch 3/3]\n",
       "    </div>\n",
       "    <table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       " <tr style=\"text-align: left;\">\n",
       "      <th>Step</th>\n",
       "      <th>Training Loss</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <td>1</td>\n",
       "      <td>4.570600</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>2</td>\n",
       "      <td>4.606000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>3</td>\n",
       "      <td>4.522700</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>4</td>\n",
       "      <td>4.318400</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>5</td>\n",
       "      <td>3.963700</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>6</td>\n",
       "      <td>3.536900</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>7</td>\n",
       "      <td>3.427900</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>8</td>\n",
       "      <td>3.264500</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>9</td>\n",
       "      <td>2.990100</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>10</td>\n",
       "      <td>2.768200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>11</td>\n",
       "      <td>2.792400</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>12</td>\n",
       "      <td>2.524200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>13</td>\n",
       "      <td>2.194100</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>14</td>\n",
       "      <td>1.986700</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>15</td>\n",
       "      <td>1.532100</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>16</td>\n",
       "      <td>1.591500</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>17</td>\n",
       "      <td>1.390400</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>18</td>\n",
       "      <td>1.129200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>19</td>\n",
       "      <td>0.783000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>20</td>\n",
       "      <td>0.614400</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>21</td>\n",
       "      <td>0.574400</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>22</td>\n",
       "      <td>0.482000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>23</td>\n",
       "      <td>0.229900</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>24</td>\n",
       "      <td>0.277100</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>25</td>\n",
       "      <td>0.142200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>26</td>\n",
       "      <td>0.120500</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>27</td>\n",
       "      <td>0.118700</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>28</td>\n",
       "      <td>0.098400</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>29</td>\n",
       "      <td>0.086400</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>30</td>\n",
       "      <td>0.062800</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>31</td>\n",
       "      <td>0.048700</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>32</td>\n",
       "      <td>0.037800</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>33</td>\n",
       "      <td>0.032300</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>34</td>\n",
       "      <td>0.023600</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>35</td>\n",
       "      <td>0.022900</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>36</td>\n",
       "      <td>0.019700</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>37</td>\n",
       "      <td>0.017900</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>38</td>\n",
       "      <td>0.016700</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>39</td>\n",
       "      <td>0.014000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>40</td>\n",
       "      <td>0.013200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>41</td>\n",
       "      <td>0.011600</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>42</td>\n",
       "      <td>0.010200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>43</td>\n",
       "      <td>0.009000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>44</td>\n",
       "      <td>0.008200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>45</td>\n",
       "      <td>0.008000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>46</td>\n",
       "      <td>0.007200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>47</td>\n",
       "      <td>0.007700</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>48</td>\n",
       "      <td>0.007400</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>49</td>\n",
       "      <td>0.006900</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>50</td>\n",
       "      <td>0.005700</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>51</td>\n",
       "      <td>0.005600</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>52</td>\n",
       "      <td>0.005900</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>53</td>\n",
       "      <td>0.005400</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>54</td>\n",
       "      <td>0.005900</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>55</td>\n",
       "      <td>0.005400</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>56</td>\n",
       "      <td>0.005600</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>57</td>\n",
       "      <td>0.004800</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>58</td>\n",
       "      <td>0.005100</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>59</td>\n",
       "      <td>0.005200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>60</td>\n",
       "      <td>0.004900</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table><p>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/root/miniconda3/envs/peft/lib/python3.10/site-packages/torch/utils/checkpoint.py:460: UserWarning: torch.utils.checkpoint: please pass in use_reentrant=True or use_reentrant=False explicitly. The default value of use_reentrant will be updated to be False in the future. To maintain current behavior, pass use_reentrant=True. It is recommended that you use use_reentrant=False. Refer to docs for more details on the differences between the two variants.\n",
      "  warnings.warn(\n",
      "/root/miniconda3/envs/peft/lib/python3.10/site-packages/torch/utils/checkpoint.py:460: UserWarning: torch.utils.checkpoint: please pass in use_reentrant=True or use_reentrant=False explicitly. The default value of use_reentrant will be updated to be False in the future. To maintain current behavior, pass use_reentrant=True. It is recommended that you use use_reentrant=False. Refer to docs for more details on the differences between the two variants.\n",
      "  warnings.warn(\n",
      "/root/miniconda3/envs/peft/lib/python3.10/site-packages/torch/utils/checkpoint.py:460: UserWarning: torch.utils.checkpoint: please pass in use_reentrant=True or use_reentrant=False explicitly. The default value of use_reentrant will be updated to be False in the future. To maintain current behavior, pass use_reentrant=True. It is recommended that you use use_reentrant=False. Refer to docs for more details on the differences between the two variants.\n",
      "  warnings.warn(\n",
      "/root/miniconda3/envs/peft/lib/python3.10/site-packages/torch/utils/checkpoint.py:460: UserWarning: torch.utils.checkpoint: please pass in use_reentrant=True or use_reentrant=False explicitly. The default value of use_reentrant will be updated to be False in the future. To maintain current behavior, pass use_reentrant=True. It is recommended that you use use_reentrant=False. Refer to docs for more details on the differences between the two variants.\n",
      "  warnings.warn(\n",
      "/root/miniconda3/envs/peft/lib/python3.10/site-packages/torch/utils/checkpoint.py:460: UserWarning: torch.utils.checkpoint: please pass in use_reentrant=True or use_reentrant=False explicitly. The default value of use_reentrant will be updated to be False in the future. To maintain current behavior, pass use_reentrant=True. It is recommended that you use use_reentrant=False. Refer to docs for more details on the differences between the two variants.\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "TrainOutput(global_step=60, training_loss=0.9513649196131155, metrics={'train_runtime': 60.5323, 'train_samples_per_second': 7.93, 'train_steps_per_second': 0.991, 'total_flos': 2430228439203840.0, 'train_loss': 0.9513649196131155, 'epoch': 3.0})"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "trainer.train()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "cc1156a8-a6c4-42f7-bbaf-a6736d51bd0f",
   "metadata": {},
   "outputs": [],
   "source": [
    "trainer.model.save_pretrained(output_dir)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "614b0c3f-0796-4b38-a228-f9d1583e2032",
   "metadata": {},
   "source": [
    "# 比较微调前后的效果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "85595b06-e8c5-475b-a2da-85eebf54dab5",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/root/miniconda3/envs/peft/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "from transformers import AutoModel, AutoTokenizer, BitsAndBytesConfig\n",
    "\n",
    "# 模型ID或本地路径\n",
    "model_name_or_path = 'THUDM/chatglm3-6b'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "462c7fa3-77e3-4e23-835c-30cc4bf4e637",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Loading checkpoint shards: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 7/7 [00:01<00:00,  5.28it/s]\n"
     ]
    }
   ],
   "source": [
    "_compute_dtype_map = {\n",
    "    'fp32': torch.float32,\n",
    "    'fp16': torch.float16,\n",
    "    'bf16': torch.bfloat16\n",
    "}\n",
    "\n",
    "# QLoRA 量化配置\n",
    "q_config = BitsAndBytesConfig(load_in_4bit=True,\n",
    "                              bnb_4bit_quant_type='nf4',\n",
    "                              bnb_4bit_use_double_quant=True,\n",
    "                              bnb_4bit_compute_dtype=_compute_dtype_map['bf16'])\n",
    "\n",
    "# 加载量化后模型(与微调的 revision 保持一致）\n",
    "base_model = AutoModel.from_pretrained(model_name_or_path,\n",
    "                                      quantization_config=q_config,\n",
    "                                      device_map='auto',\n",
    "                                      trust_remote_code=True,\n",
    "                                      revision='b098244')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "dcee89d1-542e-4b2c-845d-3a2d12fc35f4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ChatGLMForConditionalGeneration(\n",
       "  (transformer): ChatGLMModel(\n",
       "    (embedding): Embedding(\n",
       "      (word_embeddings): Embedding(65024, 4096)\n",
       "    )\n",
       "    (rotary_pos_emb): RotaryEmbedding()\n",
       "    (encoder): GLMTransformer(\n",
       "      (layers): ModuleList(\n",
       "        (0-27): 28 x GLMBlock(\n",
       "          (input_layernorm): RMSNorm()\n",
       "          (self_attention): SelfAttention(\n",
       "            (query_key_value): Linear4bit(in_features=4096, out_features=4608, bias=True)\n",
       "            (core_attention): CoreAttention(\n",
       "              (attention_dropout): Dropout(p=0.0, inplace=False)\n",
       "            )\n",
       "            (dense): Linear4bit(in_features=4096, out_features=4096, bias=False)\n",
       "          )\n",
       "          (post_attention_layernorm): RMSNorm()\n",
       "          (mlp): MLP(\n",
       "            (dense_h_to_4h): Linear4bit(in_features=4096, out_features=27392, bias=False)\n",
       "            (dense_4h_to_h): Linear4bit(in_features=13696, out_features=4096, bias=False)\n",
       "          )\n",
       "        )\n",
       "      )\n",
       "      (final_layernorm): RMSNorm()\n",
       "    )\n",
       "    (output_layer): Linear(in_features=4096, out_features=65024, bias=False)\n",
       "  )\n",
       ")"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "base_model.requires_grad_(False)\n",
    "base_model.eval()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "8b219b8e-65cc-42ec-b296-7646cd9625f9",
   "metadata": {},
   "outputs": [],
   "source": [
    "tokenizer = AutoTokenizer.from_pretrained(model_name_or_path,\n",
    "                                          trust_remote_code=True,\n",
    "                                          revision='b098244')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d70e5f8a-aa68-4be5-8a7d-c458d307349b",
   "metadata": {},
   "source": [
    "## 使用原始 ChatGLM3-6B 模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "d563e48c-8c6c-4cf5-afd2-a82b65b6b54d",
   "metadata": {},
   "outputs": [],
   "source": [
    "input_text = \"解释下乾卦是什么？\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "e4359e0f-d3cc-430f-b28d-b708efebf540",
   "metadata": {},
   "outputs": [],
   "source": [
    "response, history = base_model.chat(tokenizer, query=input_text)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "ab75d37f-2067-4528-9145-243de318fec8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "乾卦是八卦之一，代表阳、强、刚、动等含义。乾卦是由两个阴爻夹一个阳爻构成，象征天、云、雷等自然现象，以及君主、领导、刚强等人文象征。乾卦的五行属性为木，在八宫中属于北方。在占卜中，乾卦往往预示着积极、主动、进展、新开始等正面意义。同时，乾卦也提醒人们要勇往直前，克服困难，坚定信念，实现目标。\n"
     ]
    }
   ],
   "source": [
    "print(response)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "57dd8e42-38be-4b08-ba11-7f4e2172aaa2",
   "metadata": {},
   "source": [
    "## 使用微调后的 ChatGLM3-6B"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "b348384c-da99-4c12-8bcb-ebd39a8d2ee3",
   "metadata": {},
   "outputs": [],
   "source": [
    "from peft import PeftModel, PeftConfig\n",
    "\n",
    "epochs = 3\n",
    "timestamp = \"20240416_095120\"\n",
    "\n",
    "peft_model_path = f\"models/{model_name_or_path}-epoch{epochs}-{timestamp}\"\n",
    "\n",
    "config = PeftConfig.from_pretrained(peft_model_path)\n",
    "qlora_model = PeftModel.from_pretrained(base_model, peft_model_path)\n",
    "training_tag=f\"ChatGLM3-6B(Epoch=3, automade-dataset(fixed))-{timestamp}\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "367fa7d1-a3bd-44d8-ab44-252477ac9ea7",
   "metadata": {},
   "outputs": [],
   "source": [
    "def compare_chatglm_results(query, base_model, qlora_model, training_tag):\n",
    "    base_response, base_history = base_model.chat(tokenizer, query)\n",
    "\n",
    "    inputs = tokenizer(query, return_tensors=\"pt\").to(0)\n",
    "    ft_out = qlora_model.generate(**inputs, max_new_tokens=512)\n",
    "    ft_response = tokenizer.decode(ft_out[0], skip_special_tokens=True)\n",
    "    \n",
    "    print(f\"问题：{query}\\n\\n原始输出：\\n{base_response}\\n\\n\\n微调后（{training_tag}）：\\n{ft_response}\")\n",
    "    return base_response, ft_response"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "a5940d3e-6fd7-4236-b6a2-8dd0eec99494",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "问题：解释下乾卦是什么？\n",
      "\n",
      "原始输出：\n",
      "乾卦是八卦中的第一卦，由六个阳爻组成，象征着天。它所代表的是刚健、健行、刚健不屈的意境。在这个卦中，阳爻代表刚健，阴爻代表柔顺。乾卦的核心哲学是：天行健，君子拟之。君子观天行健，从而学会刚健、健行、不屈不挠的品性。乾卦的目标是周全、刚健、清洁、纯真、公允。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、健行、不屈不挠的品性。在人生的道路上，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在商战中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在人生的道路上，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在商战中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在婚恋中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在婚恋中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在求名中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、经商、求名、婚恋等方面的决策中，乾卦都代表刚健、坚毅、不屈不挠的品性。在经商中，乾卦代表刚健、坚毅、不屈不挠的品性，也象征着强烈的事业心、坚定的信念和积极进取的精神。在事业、\n",
      "\n",
      "\n",
      "微调后（ChatGLM3-6B(Epoch=3, automade-dataset(fixed))-20240416_095120）：\n",
      "[gMASK]sop 解释下乾卦是什么？ 在周易中，乾卦是六十四卦之首，由六个阳爻组成，象征着天。它所代表的是刚健、健行、刚健不屈的意境。乾卦的核心哲学是：天道刚健，运行不已，君子观此卦象，从而以天为法，自强不息。\n",
      "\n",
      "乾卦象征天，为大通而至正。得此卦者，名利双收，应把握机会，争取成果。然而，切勿过于骄傲自满，而应保持谦逊、冷静和警惕。在事业、经商、求名等方面，乾卦皆暗示着大吉大利，但也警示着必须坚持正道、修养德行，方能永远亨通。\n",
      "\n",
      "在婚恋方面，乾卦提示着阳盛阴衰，但也强调刚柔相济，相互补足，形成美满的结果。在决策方面，则是强调刚健、正直、公允，自强不息的实质，需要修养德行、坚定信念，方能克服困难，消除灾难。\n"
     ]
    }
   ],
   "source": [
    "base_response, ft_response = compare_chatglm_results(\"解释下乾卦是什么？\", base_model, qlora_model, training_tag)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
